// locutor
// (c) 2009, Normen Müller, normen.mueller@gmail.com  
// $Id: Expr.scala 86 2010-05-11 21:32:13Z normen.mueller $
package locutor.util.parsing

import collection.mutable.HashSet

/** Companion to <code>ExpressionParser</code> class.
 * 
 *  @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 *  @version 0.0.4
 */
object ExpressionParser {  
  val reserved = new HashSet[String] ++ List("visited", "nameof", "typeof", "in") ++ BaseParser.reserved
  
  val delimiters = new HashSet[String] ++ List(
    "(", ")", "{", "}", "!", "?", ":", ".", "::", "^", "&&", "||", "&", "|", "<<", ">>>", ">>", "->", "-", "/", "%", "~", "<=", "<", "==", "!=", ">", ">="    
  ) ++ BaseParser.delimiters  
}

/** Primitive GrGEN.NET expression parser.
 * 
 *  <p>At the moment only primitive string representations of GrGEN expressions are supported. No need for sophisticated AST yet due to direct looping into GrGEN.</p>
 * 
 *  @author <a href="mailto:normen.mueller@googlemail.com">Normen M&#xFC;ller</a>
 *  @version 0.0.4
 * 
 *  @thanks GrGEN.NET manual and GrGen.g#Expressions for syntax rules (v2.5.x)
 */
trait ExpressionParser extends BaseParser {  
  lazy val  expr: Parser[String] = expr_cond

  private lazy val expr_cond: Parser[String] = expr_lor ~ opt(("?" ~> expr) ~ (":" ~> expr_cond)) ^^ {
    case i ~ Some(t ~ e) => i+" ? "+t+" : "+e
    case i ~ None        => i
  }

  private lazy val expr_lor = (expr_land ~ (("||" ~ expr_land)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_land = (expr_bor ~ (("&&" ~ expr_bor)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_bor = (expr_bxor ~ (("|" ~ expr_bxor)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_bxor = (expr_band ~ (("^" ~ expr_band)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_band = (expr_except ~ (("&" ~ expr_except)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_except = (expr_eq ~ (("\\" ~ expr_eq)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_eq = (expr_rel ~ ((("==" | "!=") ~ expr_rel)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_rel = (expr_shift ~ ((("<=" | "<" | ">=" | ">" | "in" ^^^ " in ") ~ expr_shift)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_shift = (expr_add ~ ((("<<" | ">>>" | ">>") ~ expr_add)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_add = (expr_mul ~ ((("+" | "-") ~ expr_mul)*)) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_mul = expr_unary ~ ((("*" | "%" | "/") ~ expr_unary)*) ^^ {
    case expr ~ exprs =>  ((new StringBuilder(expr) /: exprs)((b, xs) => xs match {case op ~ expr => b append op append expr})).toString
  }

  private lazy val expr_unary: Parser[String] =
    ( ("~" | "!" | "-" | "+") ~ expr_unary       ^^ {case op ~ expr  => op + expr}
    | ("(" ~> primitives) ~ (")" ~> expr_unary)  ^^ {case t ~ expr   => "("+t+")"+expr}
    | (expr_primary ~ ((expr_selector)*))        ^^ {case expr ~ sel => expr + (sel mkString)}
    )

  private lazy val expr_primary =
    ( visited | nameOf | typeOf  | constant
    | (ident <~ "::") ~ ident ^^ {case l ~ r => l+"::"+r} 
    | ident    
    | "(" ~> expr <~ ")"      ^^ {case expr => "("+expr+")"}
    
    | ("{" ~> repsep((((string | numeric) <~ "->") ~ (string | numeric)), ",")) <~ "}" ^^ {
      case entries => (List[String]() /: entries)((b,e) => e match {case (key ~ value) => (key + "->" + value) :: b}).mkString("{", ",", "}")
    }
    | "{" ~> (((string | numeric) <~ opt(","))*) <~ "}" ^^ {case vals => "{"+(vals mkString ",")+"}"}
    //| "{" ~> (((string ^^ {case s => s mkString ("\"", "", "\"")} | numeric) <~ opt(","))*) <~ "}" ^^ {case vals => "{"+(vals mkString ",")+"}"}    
    )

  lazy val visited =
    ( ("visited" ~ "(") ~> ident <~ ")"                   ^^ {case id        => "visited("+id+")"}
    | ("visited" ~ "(") ~> (ident <~ ",") ~ (expr <~ ")") ^^ {case id ~ expr => "visisted("+id+","+expr+")"}
    )

  private lazy val nameOf = ("nameof" ~ "(") ~> ident <~ ")" ^^ {case id => "nameof("+id+")"}

  lazy val typeOf = ("typeof" ~ "(") ~> ident <~ ")" ^^ {case id => "typeof("+id+")"}

  //@todo in 2nd case instead of "ident" "visited" is also legal
  private lazy val expr_selector =
    ( "[" ~> expr <~ "]"                         ^^ {case expr        => "["+expr+"]"}
    | "." ~> (ident ~ opt(expr_selector_params)) ^^ {case id ~ params => "."+id+(params match {case Some(ps) => ps case _ => ""})}
    )

  private lazy val expr_selector_params = "(" ~> opt(expr ~ (("," ~> expr)*)) <~ ")" ^^ {
    case Some(expr ~ exprs) => "("+(expr :: exprs mkString ",")+")"
    case None               => "()"
  }
}
